package com.asen.business.commons.service.impl;

import com.asen.business.commons.domain.CoreBill;
import com.asen.business.commons.service.CoreBillService;
import com.asen.commons.core.base.domain.BaseEntry;
import com.asen.commons.core.base.domain.GenericEntry;
import com.asen.commons.core.base.enums.ActionTypeEnum;
import com.asen.commons.core.base.enums.BillStatusEnum;
import com.asen.commons.core.base.result.BaseResultCode;
import com.asen.commons.core.listener.ServiceContext;
import com.asen.commons.core.meta.dao.Condition;
import com.asen.commons.core.meta.entity.EntityField;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.core.utils.FieldUtils;
import com.asen.commons.exception.ServiceException;
import com.asen.commons.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 核心单据service抽象类
 *
 * @author Gary
 * @version 1.0
 * @date 2018-12-09
 */
@Slf4j
public abstract class AbstractCoreBillServiceImpl<T extends CoreBill> extends AbstractBillServiceImpl<T> implements CoreBillService<T> {

    @Override
    public void checkDupByNumber(Long id, String number) throws Exception {
        if (StringUtils.isEmpty(number)) {
            return;
        }

        Condition condition = new Condition();
        if (id != null && id > 0L) {
            condition.notEqual("id", id);
        }
        condition.equal("number", number);
        int count = count(condition);
        BaseAssert.check(count == 0, "编码【" + number + "】不能重复");
    }

    @Override
    public T getByNumber(String number) throws Exception {
        Condition condition = new Condition();
        condition.equal("number", number);
        T model = getBy(condition);

        if (model != null) {
            // 查询子对象数据
            //for (FieldObject fo : getSubFieldList()) {
            //    fo.setValue(model, getSubData(fo.getJavaType(), model));
            //}

            // 查询分录
            for (Map.Entry<Class<? extends BaseEntry>, FieldObject> entry : getEntryMap().entrySet()) {
                FieldObject fo = entry.getValue();
                fo.setValue(model, getEntry(entry.getKey(), model));
            }
        }

        return model;
    }

    @Override
    public void clearData(T model) {
        model.setId(null);
        model.setNumber(null);
        model.setStatus(null);
        model.setCreateById(null);
        model.setCreateByName(null);
        model.setCreateTime(null);
        model.setUpdateById(null);
        model.setUpdateByName(null);
        model.setUpdateTime(null);
        model.setAuditById(null);
        model.setAuditTime(null);
    }

    /**
     * 复制清除分录数据
     * @param model
     */
    @Override
    public void clearEntry(T model) {
        List<EntityField> fieldList = FieldUtils.getFieldList(model.getClass());
        for (EntityField entityField : fieldList) {
            try {
                Object value = entityField.getValue(model);
                if (value instanceof List) {
                    List list = (List) value;
                    if (CollectionUtils.isNotEmpty(list)) {
                        for (Object o : list) {
                            if (o instanceof GenericEntry) {
                                ((GenericEntry) o).setId(null);
                                ((GenericEntry) o).setParentId(null);
                                ((GenericEntry) o).setParent(null);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error("", e);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(T model) throws Exception {
        BaseAssert.notNull(model, "model不能为空");

        if (IdUtils.isNotEmpty(model.getId())) {
            update(model);
        } else {
            add(model);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void submit(T model) throws Exception {
        submitValidate(model);
        beforeSubmit(model);

        ServiceContext serviceContext = createServiceContext(model, ActionTypeEnum.SUBMIT);
        beforeListener(serviceContext);

        doSubmit(model);

        afterSubmit(model);
        afterListener(serviceContext);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void audit(Long id) throws Exception {
        BaseAssert.notEmpty(id, "ID不能为空");

        T model = get(id);

        // 前处理
        auditValidate(model);
        beforeAudit(model);

        ServiceContext serviceContext = createServiceContext(model, ActionTypeEnum.AUDIT);
        beforeListener(serviceContext);

        // 审核
        doAudit(model);

        // 后处理
        afterAudit(model);
        afterListener(serviceContext);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unAudit(Long id) throws Exception {
        BaseAssert.notEmpty(id, "ID不能为空");

        T model = get(id);

        // 前处理
        unAuditValidate(model);
        beforeUnAudit(model);

        ServiceContext serviceContext = createServiceContext(model, ActionTypeEnum.UN_AUDIT);
        beforeListener(serviceContext);

        // 反审核
        doUnAudit(model);

        // 后处理
        afterUnAudit(model);
        afterListener(serviceContext);
    }

    @Override
    protected void addValidate(T model) throws Exception {
        super.addValidate(model);

        // 默认编码不能重复
        if (StringUtils.isNotEmpty(model.getNumber())) {
            checkDupByNumber(null, model.getNumber());
        }
    }

    @Override
    protected void updateValidate(T model) throws Exception {
        super.updateValidate(model);

        // 默认编码不能重复，不能为空
        BaseAssert.notEmpty(model.getNumber(), "编码不能为空");
        checkDupByNumber(model.getId(), model.getNumber());

        // 默认已审核状态不允许修改
        Condition condition = new Condition();
        condition.equal("id", model.getId());
        condition.equal("status", BillStatusEnum.AUDITED.getValue());
        BaseAssert.check(count(condition) == 0L, "单据已审核");
    }

    @Override
    protected void removeValidate(T model) throws Exception {
        super.removeValidate(model);

        // 默认仅【保存】【提交】状态单据可以删除
        String msg = String.format("【%s】状态，不允许删除！", EnumUtils.getDesc(BillStatusEnum.class, model.getStatus()));
        BaseAssert.check(EnumUtils.in(model.getStatus(), BillStatusEnum.SAVED, BillStatusEnum.SUBMITED), msg);
    }

    @Override
    protected void beforeAdd(T model) throws Exception {
        super.beforeAdd(model);

        // 业务日期默认为当前日期
        if (model.getBizDate() == null) {
            model.setBizDate(new Date());
        }

        // 默认已保存
        if (model.getStatus() == null || EnumUtils.equal(BillStatusEnum.NEW, model.getStatus())) {
            model.setStatus(BillStatusEnum.SAVED.getValue());
        }

        if (StringUtils.isEmpty(model.getNumber())) {
            // 设置默认编码规则
            setAutoNumber(model);
        }
    }

    protected void submitValidate(T model) throws Exception {
        // 检查当前是否最新数据
        if (model.getId() != null) {
            Condition condition = new Condition();
            condition.select("id", "updateTime");
            condition.equal("id", model.getId());
            T oldModel = this.getBy(condition);
            if (oldModel == null) {
                throw new ServiceException("单据不存在或已被删除！");
            }
            if (model.getUpdateTime() != null) {
                if (model.getUpdateTime().before(oldModel.getUpdateTime())) {
                    throw new ServiceException(BaseResultCode.E0006);
                }
            }
        }
        initBaseInfo(model);
    }

    protected void beforeSubmit(T model) throws Exception {
        if (model.getStatus() == null || EnumUtils.in(model.getStatus(), BillStatusEnum.NEW, BillStatusEnum.SAVED)) {
            model.setStatus(BillStatusEnum.SUBMITED.getValue());
        }
    }

    protected void doSubmit(T model) throws Exception {
        if (IdUtils.isNotEmpty(model.getId())) {
            update(model);
        } else {
            add(model);
        }
    }

    protected void afterSubmit(T model) throws Exception {

    }

    protected void auditValidate(T model) throws Exception {
        // 【已提交或审批中】状态的单据，可审核
        String msg = String.format("【%s】状态，不允许审核！", EnumUtils.getDesc(BillStatusEnum.class, model.getStatus()));
        BaseAssert.check(EnumUtils.contains(new BillStatusEnum[]{BillStatusEnum.SUBMITED, BillStatusEnum.AUDITING}, model.getStatus()), msg);
    }

    protected void beforeAudit(T model) throws Exception {

    }

    protected void afterAudit(T model) throws Exception {

    }

    protected void doAudit(T model) throws Exception {
        model.setStatus(BillStatusEnum.AUDITED.getValue());
        // TODO
        //model.setAuditById(currentUserId());
        model.setAuditById(0L);
        model.setAuditByName(null);
        model.setAuditTime(new Date());
        updatePartial(model, "status", "auditById", "auditTime");
    }

    protected void unAuditValidate(T model) throws Exception {
        // 已审核状态单据，不允许反审核
        String msg = String.format("【%s】状态，不允许反审核！", EnumUtils.getDesc(BillStatusEnum.class, model.getStatus()));
        BaseAssert.check(EnumUtils.equal(BillStatusEnum.AUDITED, model.getStatus()), msg);
    }

    protected void beforeUnAudit(T model) throws Exception {

    }

    protected void doUnAudit(T model) throws Exception {
        model.setStatus(BillStatusEnum.SAVED.getValue());
        model.setAuditById(null);
        model.setAuditByName(null);
        model.setAuditTime(null);
        // TODO
        //model.setUpdateById(currentUserId());
        model.setUpdateById(0L);
        model.setUpdateByName(null);
        model.setUpdateTime(new Date());
        updatePartial(model, "status", "auditById", "auditByName", "auditTime", "updateById", "updateByName", "updateTime");
    }

    protected void afterUnAudit(T model) throws Exception {

    }
}
